home *** CD-ROM | disk | FTP | other *** search
- #!/usr/unsupported/bin/bash
-
- function usage ()
- {
- if [ $# -gt 0 ]; then
- echo -e "${progname}: $*\n" 1>&2
- fi
-
- shcat 1>&2 <<EOF
- Usage: ${progname} {-D} {-e maintainer} {-h} {-t time} {-v} {-x d1} {-x d2}
- {-x ...} [dir1] {dir2} {...}
- {--debug} {--errors-to=maintainer} {--help} {--time=time} {--verbose}
- {--except d1} {--except d2} {--except ...} [dir1] {dir2} {...}
-
- Options in braces are optional. Those in brackets are required.
-
- Time specifications must have units after the value, e.g. "d" for days,
- "m" for minutes. Thus, a valid time string might be "1w2d12h" which
- means look for files older than 1 week, 2 days, and 12 hours (9.5 days).
-
- Default time is 1 day. Default maintainer is ${default_maintainer}.
-
- -D, --debug Turn on shell debugging ("set -x").
- -e, --errors-to MAINTAINER Stderr is reported to maintainer via email.
- If MAINTAINER is set to "maintainer", the
- default maintainer is assumed (see above).
- -h, --help You're looking at it.
- -t, --time TIME Only PRs older than TIME will be checked
- for locks. See above for info on time format.
- -v, --verbose Chatter away while working.
- -x, --except subdirectories not to search.
-
- EOF
-
- exit 1
- }
-
- function main ()
- {
- local find_args="-mtime"
- local seconds
- local age_unit="day"
- local age
- local except_list
-
- initialize_variables "$@"
- parse_command_args "$@"
- shift $?
-
- test -n "${debug+set}" && set -x
-
- if [ $# -eq 0 ]; then
- usage "Specify at least one pathname"
- fi
-
- if ! seconds=$(timefmt_to_seconds "${time}") ; then
- exit 1
- fi
-
- age=$(seconds_to_days ${seconds})
- if [ -z "${age}" -o "${age}" -lt 1 ]; then
- find_args="-mmin"
- age_unit="minute"
- age=$(seconds_to_minutes ${seconds})
- fi
-
- if [ -n "${exceptions}" ]; then
- # Construct exception list
- for dir in ${exceptions} ; do
- except_list="${except_list} ! -path ${dir}/*"
- done
- fi
-
- (
- set -o noglob;
- exec find "$@" ${except_list} -type f -name "*.lock" ${find_args} "+${age}" -print
- ) | find_locks
-
- send_reports
- mail_stderr_to_maintainer
- }
-
- function initialize_variables ()
- {
- # Bash magic. If this variable is bound, then globbing patterns which
- # don't actually match anything will result in nothing at all, rather
- # than the pattern themselves.
- allow_null_glob_expansion=
-
- progname="${0##*/}"
- progname_arguments="$*" # Save them for stderr report
-
- bq="\`" # To prevent hairy quoting and escaping later.
- eq="'"
-
- # Path should be able to catch sendmail, which is usually in /usr/lib
- export PATH="/usr/latest/bin:${PATH}:/usr/lib"
-
- default_maintainer="brendan"
- maintainer="${default_maintainer}"
-
- time=1d # Complain about locks older than 1 day, by default.
-
- hostname=$(hostname)
-
- tmpdir="/tmp/${progname}$$"
- while [ -e "${tmpdir}" ]; do
- tmpdir="/tmp${progname}${RANDOM}"
- done
-
- TRAP_SIGNALS="EXIT SIGHUP SIGINT SIGQUIT SIGTERM"
- trap 'cleanup_and_exit' ${TRAP_SIGNALS}
-
- mkdir ${tmpdir} || exit 1
- }
-
- function parse_command_args ()
- {
- local orig_number_options=$#
- local except
-
- # unset option variables to make sure they weren't accidentally
- # exported
- unset debug stderr_file verbose exceptions
-
- # If you add new options be sure to change the wildcards below to make
- # sure they are unambiguous (i.e. only match one possible long option)
- # Be sure to show at least one instance of the full long option name to
- # document what the long option is canonically called.
- # Long options which take arguments will need a `*' appended to the
- # canonical name to match the value appended after the `=' character.
- while [ $# -gt 0 ]; do
- case z$1 in
- z-D | z--debug | z--d* )
- debug=t
- shift
- ;;
- z-e | z--errors-to* | z--er* )
- get_option_argument maintainer "$1" "$2"
- shift $?
-
- if [ "${maintainer}" = "maintainer" ]; then
- maintainer="${default_maintainer}"
- fi
-
- # Redirect all of stderr to a tmp file which we can mail
- # later.
- stderr_file="/tmp/${progname}.stderr$$"
- exec 2> "${stderr_file}"
- ;;
- z-h* | z--help | z--h* )
- usage
- ;;
- z-t | z--time | z--t* )
- get_option_argument time "$1" "$2"
- shift $?
- ;;
- z-v | z--verbose | z--v* )
- verbose=t
- shift
- ;;
- z-x | z--except | z--ex* )
- unset except
- get_option_argument except "$1" "$2"
- shift $?
-
- exceptions="${exceptions} ${except}"
- ;;
- z-- )
- shift
- break
- ;;
- z-* )
- usage "${bq}${1}${eq} is not a valid option."
- ;;
- * )
- break
- ;;
- esac
- done
-
- # Return number of shifted arguments so calling function can shift
- # appropriate amount.
- return $[ orig_number_options - $# ]
- }
-
- # Usage: get_option_argument VARIABLE OPTION ARG {OPTIONAL}
- # where VARIABLE is shell variable that will be set to the value ARG.
- # Long option syntax is `--foo=bar' or `--foo bar'. 3rd argument ARG
- # won't get used if first long option syntax was used. If 4 arg
- # OPTIONAL is non-empty, option isn't required to have an argument; if
- # the argument is missing, VARIABLE is set to the empty value.
- # Returns number of positions caller should shift
- function get_option_argument ()
- {
- local variable="$1"
- local option="$2"
- local arg="$3"
- local arg_optional="$4"
-
- # All long options must be at least 3 characters long (--o*), whereas
- # short options are only two chars (-o) and arguments are always
- # separate.
- if [ ${#option} -ge 3 -a "z${option#*=}" != "z${option}" ]; then
- arg="${option#*=}" # Strip off anything before and including `=' char
- eval ${variable}=\'"${arg}"\'
- return 1
- else
- if [ -z "${arg}" -a -z "${arg_optional}" ]; then
- usage "option ${bq}${option}${eq} requires argument."
- fi
- eval ${variable}=\'"${arg}"\'
- return 2
- fi
- }
-
- cleanup_and_exit ()
- {
- local exitstat="$?"
-
- # Reset traps to avoid double execution of this function when a signal
- # is caught (as opposed to normal exit).
- trap '' ${TRAP_SIGNALS}
-
- rm -rf "${tmpdir}" ${stderr_file} 2> /dev/null
-
- builtin exit ${exitstat}
- }
-
- #
- # Time-related functions
- #
-
- # Compute the number of seconds specified by a string of the form
- # ...y...M...w...d...h...m...s
- # For years, months, weeks, days, hours, minutes, and seconds. Any given
- # unit is optional.
- function timefmt_to_seconds ()
- {
- local arg="$*"
- local val
- local unit
- local total
-
- if [ $# -eq 0 ]; then
- read arg
- fi
-
- shift $#
- set -- $(echo ${arg} | sed 's/\([0-9]*\)\([yMwdhms]\)/\1 \2 /g')
-
- while [ $# -gt 0 ]; do
- val="${1}"
- unit="${2}"
- shift 2
-
- # `multiple' is number of seconds per unit (year, month, etc.)
- case "${unit}" in
- y ) multiple=31536000 ;;
- M ) multiple=2592000 ;;
- w ) multiple=604800 ;;
- "" | d ) multiple=86400 ;;
- h ) multiple=3600 ;;
- m ) multiple=60 ;;
- s ) multiple=1 ;;
- * )
- usage "Invalid time+unit argument: ${val}${unit}"
- return 1
- ;;
- esac
-
- total=$[ total + (val * multiple) ];
- done
-
- echo "${total}"
- }
-
- function seconds_to_days ()
- {
- echo $[ ${1:-0} / 86400 ];
- }
-
- function seconds_to_minutes ()
- {
- echo $[ ${1:-0} / 60];
- }
-
- #
- # Routines which search for locks
- #
-
- function find_locks ()
- {
- verbose_echo "Searching for locks..."
-
- while read file; do
- verbose_echo "Looking for locks in ${file}"
-
- lock_info=$(gnatslocks "${file}")
-
- if [ -n "${lock_info}" ]; then
- file="`echo ${lock_info%%:*} | sed -e s,/gnats/GNATS/,,g`"
- user="${lock_info#*:\ }"
- user="${user%%@*}"
-
- verbose_echo "*** PR ${file} is locked by ${bq}${user}${eq}"
-
- if user_known_p "${user}" ; then
- echo " ${file}" >> "${tmpdir}/${user}.K"
- else
- owner=$(ls -l "${file}" | awk '{print $3}')
- echo "${file} by ${user}" >> "${tmpdir}/${owner}.U"
- fi
- fi
-
- done
-
- verbose_echo "Done searching for locks."
- }
-
- # Give some info about each lock.
- function gnatslocks ()
- {
- local file
-
- for file in "$@" ; do
- filename=`echo ${file} | sed -e s/\.lock//g`
- echo "${filename}: `cat ${file}`"
- done
- }
-
- function user_known_p ()
- {
- local file
-
- for file in /etc/passwd ; do
- if grep -s "^${1}:" "${file}" > /dev/null 2>&1 ; then
- return 0
- fi
- done
-
- return 1
- }
-
- #
- # Reporting generating routines
- #
-
- function send_reports ()
- {
- local file
-
- pushd "${tmpdir}" > /dev/null
-
- for file in *.[KU] ; do
- verbose_echo "*** Mailing report to ${file%.[KU]}"
- generate_mail_message "${file}" | sendmail -oi -t
- done
-
- popd > /dev/null
- }
-
- function generate_mail_message ()
- {
- local file="$1"
- local recipient="${file%.[KU]}"
- local line
-
- shcat <<- __EOF__
- From: ${progname} (GNATS lock monitor daemon)
- To: ${recipient}
- Bcc: ${maintainer}
- Reply-To: ${maintainer}
- Subject: old GNATS locks
- Precedence: bulk
-
- This is an automated report generated on ${hostname}.
-
- __EOF__
-
- case "${file}" in
- "*.K" )
- shcat <<- __EOF__
- You have had some PRs locked for over $(age_echo ${age} ${age_unit}).
- If you no longer need the PR locked, just do \'C-x k\' in the buffer
- to relinquish the lock. However, if the original emacs session you
- had no longer exists (e.g, emacs crashed, or you did a kill-buffer
- by hand, you have a couple of options. You can either type:
-
- /usr/unsupported/lib/gnats/pr-edit --unlock foo/1234
-
- for each one, or, in emacs, you can do
-
- M-x unlock-pr RET foo/1234 RET
-
- to unlock them. The PRs in question are:
- __EOF__
- ;;
- "*.U" )
- shcat <<- __EOF__
- The following files are presently locked by unknown users, but
- you (${recipient}) are the present owner of the PR. These files
- have been locked over $(age_echo ${age} ${age_unit}). Please look into this.
- __EOF__
- ;;
- esac
-
- echo ""
- shcat "${file}"
- echo ""
- shcat <<- __EOF__
- Thanks,
- Your friendly neighborhood GNATS admin.
- __EOF__
-
- }
-
- function age_echo ()
- {
- local age="$1"
- local unit="$2"
-
- echo -n "${age} "
- if [ "${age}" = "1" ]; then
- echo "${unit}"
- else
- echo "${unit}s"
- fi
- }
-
- function mail_stderr_to_maintainer ()
- {
- if [ -z "${stderr_file+set}" ]; then return 0; fi
-
- if [ -s "${stderr_file}" ]; then
- sendmail -oi -t <<- __EOF__
- From: ${progname} (GNATS lock monitor daemon)
- To: ${maintainer}
- Subject: ${progname} stderr output
- Precedence: bulk
-
- This is an automated report from host ${hostname}.
- With euid ${EUID} (ruid ${UID}), program "${progname}" ran with
- the following arguments:
-
- ${progname_arguments}
-
- and generated the following output on stderr:
-
- $(cat "${stderr_file}")
- __EOF__
- fi
- }
-
- function shcat ()
- {
- local IFS=""
- local line
- local file
- local exitstat=0
-
- if [ $# -eq 0 ]; then
- while read line; do
- echo "${line}"
- done
- return 0
- else
- for file in "$@" ; do
- if [ -r "${file}" ]; then
- {
- while read line; do
- echo "${line}"
- done
- } < "${file}"
- else
- # This will cause the error to be printed on stderr
- < "${file}"
- exitstat=1
- fi
- done
- return ${exitstat}
- fi
- }
-
- function verbose_echo ()
- {
- test -n "${verbose+set}" && echo "$@" 1>&2
- }
-
- function exit ()
- {
- exitstat="$1"
- builtin exit ${exitstat}
- }
-
- main "$@"
-
- #
- # eof
- #
-